Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[utils] Add component propType #13816

Merged
merged 2 commits into from
Dec 10, 2018

Conversation

eps1lon
Copy link
Member

@eps1lon eps1lon commented Dec 5, 2018

It may be obvious from the prop name what's expected but using react-is is more robust against future type changes of accepted values for React.createElement. It also provides a more helpful error message in case something different was passed. React already has a pretty helpful message but it will help narrow down the issue in case we apply some conditional logic to the component prop e.g. in ListItem.

Todo:

  • use wherever applicable once general direction is accepted.

@eps1lon eps1lon added docs Improvements or additions to the documentation core labels Dec 5, 2018
Copy link
Member

@oliviertassinari oliviertassinari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my perspective, we only miss process.env.NODE_ENV checks to obliviate the bundle size implication. It would also be great to use it everywhere. Related to facebook/prop-types#200.

@@ -149,6 +149,10 @@ function generatePropType(type) {
return generatePropType(chained);
}

if (type.raw === 'componentProp') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, nice trick!

@eps1lon
Copy link
Member Author

eps1lon commented Dec 5, 2018

Yep need to investigate the bundle size increase. I was hoping it would be fully wrapped due to babel-transform-react-remove-prop-types.

@oliviertassinari
Copy link
Member

I was hoping it would be fully wrapped due to babel-transform-react-remove-prop-types.

We don't handle it, at least not with the wrap mode. I have been using the following pattern so far:
/~https://github.com/mui-org/material-ui/blob/6f4869af638a61acab480ccb749681aa1c4fa903/packages/material-ui/src/utils/chainPropTypes.js#L1-L5

@eps1lon
Copy link
Member Author

eps1lon commented Dec 6, 2018

The issue is again that we don't transpile to esmodules. Lines like
/~https://github.com/mui-org/material-ui/blob/master/packages/material-ui-styles/src/install.js#L3
will pull in the complete @material-ui/utils package. It's time to push #13391 and abandon the flat package structure for @material-ui/styles. It serves no purpose.

@oliviertassinari
Copy link
Member

the flat package structure

What's the link? I agree that we should push the tree shaking story forward!

Lines like

If it wasn't for size-limit I would have changed the import.

@eps1lon
Copy link
Member Author

eps1lon commented Dec 7, 2018

the flat package structure

What's the link? I agree that we should push the tree shaking story forward!

If you still want to support imports via import module from '@material-ui/styles/module' that are also tree-shakeable we need to put those in a folder again with a package.json and main and module entries.

There is an alternative with *.mjs files but as far as I remember those could cause some trouble. I would like to try it out though. It's marked as alpha for a reason so we can experiment with it.

@oliviertassinari
Copy link
Member

If you still want to support imports via import module from '@material-ui/styles/module' that are also tree-shakeable

Why would we want to make nested import tree-shakeable? A top level tree shakeable package sounds good enough to me.

@oliviertassinari oliviertassinari force-pushed the feat/core/component-prop-type branch from bf358af to d60d0f4 Compare December 8, 2018 18:08
@oliviertassinari
Copy link
Member

oliviertassinari commented Dec 8, 2018

Regarding the tree shaking. I think that we can try the react-router approach. They use a full path import where the path change between the esmodule and the commonjs version:

@oliviertassinari oliviertassinari force-pushed the feat/core/component-prop-type branch from d60d0f4 to 3ab7fd3 Compare December 8, 2018 18:26
@oliviertassinari oliviertassinari changed the title [utils] Add component propType [WIP] [utils] Add component propType Dec 8, 2018
@oliviertassinari oliviertassinari force-pushed the feat/core/component-prop-type branch from 3ab7fd3 to 8da2fdf Compare December 10, 2018 22:17
@oliviertassinari oliviertassinari force-pushed the feat/core/component-prop-type branch from 8da2fdf to 58faaeb Compare December 10, 2018 22:44
@oliviertassinari oliviertassinari merged commit 5be50c8 into mui:master Dec 10, 2018
@eps1lon eps1lon deleted the feat/core/component-prop-type branch December 11, 2018 08:47
@eps1lon
Copy link
Member Author

eps1lon commented Dec 11, 2018

Why would we want to make nested import tree-shakeable? A top level tree shakeable package sounds good enough to me.

The goal should be to make everything tree-shakeable. You don't want to bother yourself with package structure everytime you import something. If you make the top level tree-shakeable you should also make everything below tree-shakeable. You want everything that is importing something to be tree-shakeable. It's the reason why we have a bundle increase in Paper when nothing changed for that file except that @material-ui/utils increased in size. If Paper would be shakeable then webpack (or rather uglify) would be able to remove componentProp from the bundle. But since Paper is in commonJS webpack will include the full @material-ui/utils bundle.

That's one of the benfits of tree shaking. Bundle size never increases unless something observable changes. Right now it can increase if any of the packages imported in @material-ui/core/* increases its bundle size without actually changing anything.

@oliviertassinari
Copy link
Member

The goal should be to make everything tree-shakeable.

@eps1lon Oh, I haven't realized that tree-shaking isn't transitive. That it will work for import { Paper } from '@material-ui/core', but not with the @material-ui/utils import that is within the Paper component 😱. We definitely need to look into that.

@eps1lon
Copy link
Member Author

eps1lon commented Dec 11, 2018

TL;DR: Given a file file.js import { value } from 'module'; in source. If module is in esmodule format*
and file is transpiled with esmodule format then only value will be included in the bundle. Otherwise the import statement is WRT to bundle size equivalent to import * as module from 'module';
* and module has no side-effects WRT to require

In general tree-shaking will only work (in webpack) with esmodules. The key difference is esmodules use static imports via import while commonJS uses dynamic imports via require. So webpack can identify at compile time what imports are used. For dynamic imports you you need heuristics (or config) to know what's being used and what not (prepack might be able to help here too).

So webpack knows that it can eliminate all imports in @material-ui/index except Paper if I write import { Paper } from '@material-ui/core' but when it processes Paper (in commonJS) it will use the full @material-ui/utils package because because import { componentProps } from '@material-ui/utils' will be transpiled to

var _utils = _interopRequireDefault(require("@material-ui/utils"));
var componentProp = _utils.componentProp;

and webpack will bundle the full package. It would be unsafe to shake everything else from @material-ui/utils since (see dynamic vs. static) you could write something like

var _utils = _interopRequireDefault(require("@material-ui/utils"));
var componentProp = _utils.componentProp;
var myUtilsFunction = _utils[someInputStringFromTheRuntime]; // maybe at runtime this would evaluate to 'exactProp'
// unsafe tree shaking would result in _utils: { componentProps: Function, exactProp: undefined }
myUtilsFunction(); // TypeError: myUtilsFunction is not callable 

With #13391 however this would be transpiled to (or rather no transpilation at import level)
import { componentProps } from '@material-ui/utils' and webpack would be able to shake @material-ui/utils.

I'll rebase #13391 and update eps1lon/material-ui-treeshaking. If I understood this correctly then I should be able to demonstrate the difference in bundle size on the @material-ui/utils bundle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Improvements or additions to the documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants